package com.xaicode.learn.bio;

import com.xaicode.learn.util.timer.ThreadPoolTimer;
import com.xaicode.learn.util.timer.TimerUtil;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;

import static com.xaicode.learn.util.timer.ThreadPoolTimer.Type.ACTIVE_COUNT;
import static com.xaicode.learn.util.timer.TimerUtil.MIN_1;
import static com.xaicode.learn.util.timer.TimerUtil.ZERO;

/**
 * @author Locker xaicode@sina.com
 * @version 1.0
 * @date 2021/6/12 0012 22:41
 */
@Slf4j
public class BIOServer {

    private static final Integer SERVER_PORT = 6666;

    public static void main(String[] args) throws IOException {

        /*
            1. create thread pool
            2. create a new socket to connect if there is a new client connected
         */

        ExecutorService executorService = Executors.newCachedThreadPool();

        // check thread pool active thread count
        TimerUtil.run(new ThreadPoolTimer((ThreadPoolExecutor) executorService, ACTIVE_COUNT), ZERO, MIN_1);

        // create socket
        ServerSocket serverSocket = new ServerSocket(SERVER_PORT);
        log.info("server started on {}", SERVER_PORT);

        while (true) {
            // listen client socket
            // The method accept() blocks until a connection is made
            final Socket socket = serverSocket.accept();
            log.info("find a new socket");
            // create a new thread
            executorService.execute(new Runnable() {
                public void run() {
                    handler(socket);
                }
            });
        }
    }

    /**
     * handler to connect client socket
     *
     * @param socket client socket
     */
    public static void handler(Socket socket) {
        // to read
        byte[] bytes = new byte[1024];
        // thread name
        log.info("current thread id is [{}]", Thread.currentThread().getId());

        try {
            // get input stream
            InputStream inputStream = socket.getInputStream();
            while (true) {

                // check close
                if (socket.isClosed()) {
                    log.info("client [{}] is closed", Thread.currentThread().getId());
                    break;
                }

                /*
                    the total number of bytes read into the buffer,
                    or -1 if there is no more data because the end of the stream has been reached
                 */
                int read = inputStream.read(bytes);
                if (read != -1) {
                    // continue read
                    // win10: use 'ctrl + ]' and command 'send [content]' will send a full content
                    log.info("client [{}] send content: [{}]", Thread.currentThread().getId(), new String(bytes, 0, read));
                } else {
                    break;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                log.info("client [{}] is closed", Thread.currentThread().getId());
            }
        }
    }

}
